<template>
  <div class="sh-tree" v-if="treeData.length > 0">
    <ul v-for="(node, i) in treeData" :key="i">
      <li
        :class="['child-node', commonData.dropType]"
        :style="{paddingLeft: paddingLeft}"
        :draggable="draggable"
        @dragstart.stop="handleDragStart(node, $event)"
        @dragend.stop="handleDragEnd" >
        <div></div>
        <div
          :class="['node-content', {'active-li': node === commonData.activeNode}]"
          @dragenter.prevent.stop="handelDragEnter(i, $event)"
          @dragover.prevent.stop
          @drop.prevent.stop="handleDrop(i, $event)" >
          <span :class="['icon', node.expand ? 'rotate180-enter icon-expand' : 'rotate180-leave icon-unexpand']">
            <Icon :type="icon" v-if="hasChild(node)" @click="clickIcon(node)" />
          </span>
          <div
            :class="['inner-wrap', {'drop-inner': isDrop(node)}]"
            @click.stop="clickContent(node)" >
            <Checkbox
              style="margin-right: 10px;"
              v-if="showCheckbox"
              v-model="node.checked"
              :indeterminate="countState(node)"
              :disabled="node.disabled"
              @on-change="changeCheckBox(node)" />
            <Render v-if="renderTreeNode" :render="renderTreeNode" :node="node" />
            <slot v-else :node="node">
                <span >{{node.name}}</span>
            </slot>
          </div>
        </div>
        <!-- vue2.6+使用v-slot,方便兼容vue3.0 -->
        <transition name="transition-drop">
          <Tree
            v-show="node.expand"
            class="sh-tree"
            :level="1 + level"
            :changeData="changeData"
            :draggable="draggable"
            :treeData="node.children"
            :commonData="commonData"
            :renderTreeNode="renderTreeNode"
            :showCheckbox="showCheckbox">
            <template v-slot="{ node }">
              <slot :node="node">
                <span >{{node.name}}</span>
              </slot>
            </template>
          </Tree>
        </transition>
      </li>
    </ul>
  </div>
</template>

<script>
import Icon from '../../icon'
import Render from './render'
/**
 * 当前更新数据采用集体遍历，后续待优化。。。
 * 父组件采用事件形式更新数据，子组件采用递归形式冯新数据
 */
export default {
  name: 'Tree',
  components: {
    Icon,
    Render
  },
  props: {
    level: {
      type: Number,
      default: 0
    },
    renderTreeNode: {
      type: Function
    },
    icon: {
      type: String,
      default: 'icon-down'
    },
    showCheckbox: {
      type: Boolean,
      default: false
    },
    changeData: { // 是否需要响应拖拽
      type: Boolean,
      default: false
    },
    draggable: {
      type: Boolean,
      default: false
    },
    treeData: {
      type: Array,
      default: () => {
        return []
      }
    },
    commonData: {
      default: () => {
        return {
          activeNode: {}, // 激活节点
          target: [], // 拖动组件
          dropData: {}, // 拖动数据
          dropType: null, // 拖动类型[插入，禁止]（插入子节点，禁止拖放）
          dropNode: null // 拖放容器
        }
      }
    },
    searchVal: {
      type: [Number, String]
    }
  },
  data () {
    return {
      activeNode: {}
    }
  },
  computed: {
    paddingLeft () {
      return this.firstCompoents()
        ? 0
        : null
    }
  },
  mounted () {
    this.activeNode = this.commonData
  },
  methods: {
    firstCompoents () {
      return this.$parent.$options.name !== 'Tree'
    },
    hasChild (node) {
      return node.children && node.children.length > 0
    },
    isDrop (node) {
      return node === this.commonData.dropNode && node !== this.commonData.dropData
    },
    emitChecked (node) {
      if (!this.firstCompoents()) {
        this.$parent.emitChecked(node)
      } else {
        this.$nextTick(() => {
          this.countCheckedArr(node)
        })
      }
    },
    emitEvent (event, params) {
      if (!this.firstCompoents()) {
        this.$parent.emitEvent(event, params)
      } else {
        this.$emit(event, params)
      }
    },
    clickContent (node) {
      this.$set(this.activeNode, 'activeNode', node)
      this.emitEvent('on-selected-change', node)
    },
    clickIcon (node) {
      this.$set(node, 'expand', !node.expand)
    },
    /**
     * 节点拖拽事件
    */
    handleDragStart (item, event) {
      item.level = this.level
      this.$set(this.activeNode, 'dropData', item)
      this.activeNode.target = this.treeData
      // event.dataTransfer.effectAllowed = 'copy'
      // event.dataTransfer.setData('Text', JSON.stringify(this.treeData))
    },
    handelDragEnter (index, event) {
      const target = this.treeData[index]
      if (!target.expand) {
        this.$set(target, 'expand', true)
      }
      if (this.level !== this.activeNode.dropData.level) {
        this.$set(this.activeNode, 'dropNode', null)
        this.$set(this.activeNode, 'dropType', 'disabled')
      } else {
        this.$set(this.activeNode, 'dropNode', target)
        this.$set(this.activeNode, 'dropType', 'inset')
      }
      // event.dataTransfer.dropEffect = 'copy'
    },
    handleDrop (index, event) {
      const target = this.treeData[index]
      if (target !== this.activeNode.dropData && this.activeNode.dropType !== 'disabled') {
        if (this.changeData) {
          this.dropCallback(index)
        }
        this.emitEvent('on-drop', {
          parentNode: target,
          targetNode: this.activeNode.dropData,
          index: index, // 添加下标
          callback: this.dropCallback
        })
      }
      // console.log('handleDrop', JSON.parse(event.dataTransfer.getData('Text')))
    },
    dropCallback (index, node = this.activeNode.dropData) {
      let _index = 0
      this.activeNode.target.forEach((obj, i) => {
        if (obj === this.activeNode.dropData) {
          _index = i
        }
      })
      if (this.treeData === this.activeNode.target) {
        [this.treeData[index], this.treeData[_index]] = [this.treeData[_index], this.treeData[index]]
      } else {
        this.treeData.splice(index, 0, node)
        this.activeNode.target.splice(_index, 1)
      }
      this.$set(this.activeNode, 'dropData', null)
    },
    handleDragEnd () {
      this.$set(this.activeNode, 'dropNode', null)
      this.$set(this.activeNode, 'dropType', null)
    },
    /**
     * 查找所有子孙组件（可选枝干组件）
     * 查找结果平级展示
    */
    searchNode () {},
    /**
     * 父级组件使用事件监听
     * 子孙后代使用递归查找
    */
    countState (node) {
      let result = false
      if (this.hasChild(node) && this.showCheckbox) {
        let checkAll = true
        node.children.forEach(obj => {
          if (obj.checked || obj.halfelEction) {
            result = true
          }
          if (!obj.checked) {
            checkAll = false
          }
        })
        this.$set(node, 'checked', checkAll)
      }
      this.$set(node, 'halfelEction', result)
      return (result && !node.checked)
    },
    changeCheckBox (node) {
      let stack = []// 同来存放将来要访问的节点
      stack.push(node)
      let num = 1 // 当前节点数据不改变
      while (stack.length !== 0) {
        let item = stack.pop()// 正在访问的节点
        if (num !== 1 && !item.disabled) {
          this.$set(item, 'checked', !!node.checked)
        }
        let childrens = item.children
        for (let i = childrens.length - 1; i >= 0; i--) { // 将现在访问点的节点的子节点存入stack，供将来访问
          stack.push(childrens[i])
        }
        num++
      }
      this.emitChecked(node)
    },
    countCheckedArr (obj, arr = this.treeData) {
      let result = []
      if (arr.length > 0) {
        arr.forEach(node => {
          let stack = [node]
          while (stack.length !== 0) {
            let item = stack.pop()
            if (item.checked) {
              result.push(item)
            }
            if (this.hasChild(item)) {
              let childrens = item.children
              for (let i = childrens.length - 1; i >= 0; i--) {
                stack.push(childrens[i])
              }
            }
          }
        })
      }
      this.$emit('on-checked-change', { node: obj, selectedData: result })
      return result
    }
  }
}
</script>
